/*
 * 
 * 
 * 
 */
package org.jjliu.example.dao;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.jjliu.example.dao.Filter.Operator;
import org.jjliu.example.dao.Order.Direction;
import org.jjliu.example.entity.ISoftDeleteEnable;
import org.springframework.stereotype.Repository;

import com.sun.org.apache.bcel.internal.generic.GETSTATIC;


/**
 * Dao - 基类
 * 
 * 
 * 
 */

@Repository
public class HibernateDao {

    @Resource
    SessionFactory sessionFactory;


	private int checkFrom(String sql){
		if(sql==null)return -1;
		int leftbracket = 0;
		for(int i=0;i<sql.length();i++){
			char c= sql.charAt(i);
			if(c=='('){
				leftbracket++;
			}else if(c==')'){
				leftbracket--;
			}else if(c=='F' || c=='f'){
				char c1= sql.charAt(i+1);
				char c2= sql.charAt(i+2);
				char c3= sql.charAt(i+3);
				if(
						(c1=='R' || c1=='r')
						&&(c2=='O' || c2=='o')
						&&(c3=='M' || c3=='m')
						&& (leftbracket == 0)
				){
					return i;
				}
			}
		}
		return -1;
	}
	private String[] splitFrom(String sql){
		String[] res = new String[2];
		int index = checkFrom(sql);
		if(index!=-1){
			res[0]=sql.substring(0,index);
			res[1]=sql.substring(index+4, sql.length());
		}
		return res;
	}
	private boolean moreSelectColumns(String selectWhat){
		if(StringUtils.isBlank(selectWhat))return false;
		int leftbracket = 0;
		for(int i=0;i<selectWhat.length();i++){
			char c= selectWhat.charAt(i);
			if(c=='('){
				leftbracket++;
			}else if(c==')'){
				leftbracket--;
			}else if(c==',' && leftbracket == 0){
				return true;
			}
		}
		return false;
	}

	private void setResultTransformer(String hql, Query query) {
		if(moreSelectColumns(splitFrom(hql)[0])){
			query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		}else{
			query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		}
	}

	private org.hibernate.criterion.Order getOrder(Order order){
		if(order.getDirection() == Direction.asc){
			return org.hibernate.criterion.Order.asc(order.getProperty());
		}else{
			return org.hibernate.criterion.Order.desc(order.getProperty());
		}
	}
	
	private Criterion getCriterion(Filter filter){
		if (filter.getOperator() == Operator.eq && filter.getValue() != null) {
			return Restrictions.eq(filter.getProperty(),filter.getValue());
		} else if (filter.getOperator() == Operator.ne && filter.getValue() != null) {
			return Restrictions.ne(filter.getProperty(),filter.getValue());
		} else if (filter.getOperator() == Operator.gt && filter.getValue() != null) {
			return Restrictions.gt(filter.getProperty(),filter.getValue());
		} else if (filter.getOperator() == Operator.lt && filter.getValue() != null) {
			return Restrictions.lt(filter.getProperty(),filter.getValue());
		} else if (filter.getOperator() == Operator.ge && filter.getValue() != null) {
			return Restrictions.ge(filter.getProperty(),filter.getValue());
		} else if (filter.getOperator() == Operator.le && filter.getValue() != null) {
			return Restrictions.le(filter.getProperty(),filter.getValue());
		} else if (filter.getOperator() == Operator.like && filter.getValue() != null && filter.getValue() instanceof String) {
			return Restrictions.like(filter.getProperty(),(StringUtils.contains((String)filter.getValue(), '%'))?filter.getValue():("%"+filter.getValue()+"%"));
		} else if (filter.getOperator() == Operator.in && filter.getValue() != null) {
			return Restrictions.in(filter.getProperty(),(Object[])filter.getValue());
		} else if (filter.getOperator() == Operator.isNull) {
			return Restrictions.isNull(filter.getProperty());
		} else if (filter.getOperator() == Operator.isNotNull) {
			return Restrictions.isNotNull(filter.getProperty());
		}
		return Restrictions.eq(filter.getProperty(),filter.getValue());
	}

	
	public SessionFactory getCurrentSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public <T> Criteria getCriteria(CriterionModel criterionModel, PageModel pageModel) {
		if(pageModel==null){
			pageModel = new PageModel().forbidPage();
		}
		Criteria criteria = getCurrentSession().createCriteria(criterionModel.getClazz());
		if(CollectionUtils.isNotEmpty(criterionModel.getFilters())){
			for(Filter filter : criterionModel.getFilters()){
				Criterion criterion = getCriterion(filter);
				criteria.add(criterion);
			}
		}
		if(CollectionUtils.isNotEmpty(criterionModel.getOrders())){
			for(Order order : criterionModel.getOrders()){
				criteria.addOrder(getOrder(order));
			}
		}
		criteria.setFirstResult(pageModel.getFirstResult());
		criteria.setMaxResults(pageModel.getPageSize());
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		return criteria;
	}


	public Query getQuery(HqlModel hqlModle, PageModel pageModel){
		if(pageModel==null){
			pageModel = new PageModel().forbidPage();
		}
		Session session = getCurrentSession();
		hqlModle.build();
		Query query = session.createQuery(hqlModle.getBuildHql());
		for(Entry<String, Object> entry : hqlModle.getBuildMap().entrySet()){
			String key = entry.getKey();
			Object value = entry.getValue();
			if(value instanceof Object[]){
				query.setParameterList(key, (Object[])value);
			}else if(value instanceof Collection<?>){
				query.setParameterList(key, (Collection<?>)value);
			}else{
				query.setParameter(key,value);
			}
		}
		query.setFirstResult(pageModel.getFirstResult());
		query.setMaxResults(pageModel.getPageSize());
		if(moreSelectColumns(hqlModle.getSelectColumns())){
			query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		}else{
			query.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		}

		return query;
	}

	public Query getQuery(String hql, Object... params){
		Session session = getCurrentSession();
		Query query = session.createQuery(hql);
        for (int i = 0; i < params.length; i++) {
        	query.setParameter(i, params[i]);
        }
        setResultTransformer(hql,query);
		return query;
	}
	
	
	
	
	public Session getCurrentSession(){
		Session session = sessionFactory.getCurrentSession();
		return session;
	}

	
	public<T> long size(CriterionModel criterionModel){
		Criteria criteria = getCriteria(criterionModel,null);
		long total = ((Number)criteria.setProjection(Projections.rowCount()).uniqueResult()).longValue();
		return total;
	}
	public<T> boolean exist(CriterionModel criterionModel){
		Criteria criteria = getCriteria(criterionModel,new PageModel().setPageSize(1));
		long total = ((Number)criteria.setProjection(Projections.rowCount()).uniqueResult()).longValue();
		return total>0;
	}
	public <T> void save(T t){
		getCurrentSession().save(t);
	}
	
	public <T> void update(T t){
		getCurrentSession().update(t);
	}
	
	public <T> void saveOrUpdate(T t){
		getCurrentSession().saveOrUpdate(t);
	}
	
	public <T> void delete(T t){
		getCurrentSession().delete(t);
	}
	
	public <T> void softDelete(T t){
		if(t instanceof ISoftDeleteEnable){
			((ISoftDeleteEnable)t).setSoftDeleted();
			getCurrentSession().update(t);
		}else{
			throw new HibernateException("can't softDelete "+t);
		}
	}
	
	
	public <T> T get(Class<?> clazz,Serializable id) {
		return (T) getCurrentSession().get(clazz, id);
	}
	
	public <T> T find(CriterionModel criterionModel) {
		Criteria criteria = getCriteria(criterionModel,new PageModel().setPageSize(1));
		T t =null;
		try {
			t = (T) criteria.uniqueResult();
		} catch (HibernateException e) {
		}
		return t;
	}
	public <T> List<T> query(CriterionModel criterionModel, PageModel page) {
		if(page==null){
			page = new PageModel().forbidPage();
		}
		Criteria criteria = getCriteria(criterionModel,page);
		List<T> list = criteria.list();
		page.setContent(list);
		if(page.isForbidPage()){
			page.setTotal(list.size());
		}else{
			long total = ((Number)criteria.setProjection(Projections.rowCount()).uniqueResult()).longValue();
			page.setTotal(total);
		}
		return list;
	}
	public<T> T find(HqlModel hqlModel){
		Query query = getQuery(hqlModel, new PageModel().setPageSize(1));
		try {
			return (T)query.uniqueResult();
		} catch (Exception e) {
		}
		return null;
	}
	public<T> List<T> query(HqlModel hqlModel,  PageModel page){
		if(page==null){
			page = new PageModel().forbidPage();
		}
		Query query = getQuery(hqlModel,page);
		List<T> list = query.list();
		page.setContent(list);
		if(page.isForbidPage()){
			page.setTotal(list.size());
		}else{
			Query queryCount=null;
			if(hqlModel.isDistinct() || StringUtils.isNotBlank(hqlModel.getGroupBy()) ){
				String countStr = "select count(1) as num from ( "+query.getQueryString()+" ) as __hql__temp__";
				HqlModel hqlModelCount = new HqlModel(countStr).addWhereParams(hqlModel.getBuildMap());
				queryCount = getQuery(hqlModelCount,null);
			}else{
				try {
					HqlModel hqlModelCount = (HqlModel) hqlModel.clone();
					hqlModelCount.setSelectColumns("count(1) as num");
					queryCount = getQuery(hqlModelCount,null);
				} catch (CloneNotSupportedException e) {
					throw new RuntimeException(e);
				}
			}
			long total = ((Number)queryCount.uniqueResult()).longValue();
			page.setTotal(total);
		}
		return list;
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	public<T> T find(String hql,Object... params){
		Query query = getQuery(hql, params);
		query.setMaxResults(1);
//		setResultTransformer(hql, query);
		try {
			return (T)query.uniqueResult();
		} catch (Exception e) {
		}
		return null;
	}
	public<T> List<T> query(String hql,Object... params){
		Query query = getQuery(hql, params);
//		setResultTransformer(hql, query);
		return (List<T>)query.list();
	}
	
	public<T> List<T> testquery(String hql,Object... params){
		Query query = getCurrentSession().createQuery(hql);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
//		setResultTransformer(hql, query);
		List list =  (List<T>)query.list();
		return list;
	}
	
	
	
	
	
}